const express = require('express')
const http = require('http')
const { WebSocketServer, WebSocket } = require('ws')
const crypto = require('crypto')
const path = require('path')
const { TextDecoder, TextEncoder } = require('util')
const app = express()
const httpServer = http.createServer(app)
const wss = new WebSocketServer({ port: 3001 }) // 独立WebSocket端口

// 配置
const PORT = 3000
const TANK_SPEED = 250 // 坦克移动速度
const BULLET_SPEED = 20 // 子弹飞行速度
const TANK_SIZE = 40 // 坦克大小
const BULLET_SIZE = 10 // 子弹大小
const MAX_PLAYERS = 10 // 最大玩家人数
const UPDATE_INTERVAL = 33 // 约30fps
const SHOOT_COOLDOWN = 500

// 游戏状态
let gameState = {
  width: 0,
  height: 0,
  players: {},
  bullets: [],
  obstacles: [],
  status: 'waiting',
  scores: {}
}
const playerMap = {} // 玩家ID到WebSocket客户端的映射
const clients = new Map() // WebSocket客户端到玩家ID的映射

// 初始化障碍物
function generateObstacles(width, height) {
  const count = Math.floor((width * height) / 100000)
  return Array.from({ length: count }, () => ({
    x: Math.floor((Math.random() * 100) % (width / 60)) * 60,
    y: Math.floor((Math.random() * 100) % (height / 60)) * 60,
    width: 60,
    height: 60
  }))
}

// 碰撞检测
function checkCollision(rect1, rect2) {
  return (
    rect1.x < rect2.x + rect2.width &&
    rect1.x + rect1.width > rect2.x &&
    rect1.y < rect2.y + rect2.height &&
    rect1.y + rect1.height > rect2.y
  )
}

// 方向转角度
function getAngleFromDirection(direction) {
  return {
    up: -Math.PI / 2,
    down: Math.PI / 2,
    left: Math.PI,
    right: 0
  }[direction]
}

// 边界检测
function clampPosition(x, y, width, height, size) {
  return {
    x: Math.max(0, Math.min(x, width - size)),
    y: Math.max(0, Math.min(y, height - size))
  }
}

// 处理聊天
function handleChat(data) {
  if (data.target === 'room') {
    wss.clients.forEach(client => {
      client.send(JSON.stringify({ type: 'chat', data: data }))
    })
  } else {
    const chatTo = Object.values(gameState.players).filter(p => p.id === data.target || p.playerName === data.target)
    if (chatTo.length > 0) {
      if (playerMap[data.sender.id]) {
        playerMap[data.sender.id].send(JSON.stringify({ type: 'chat', data: data }))
      }
      if (playerMap[chatTo[0].id]) {
        playerMap[chatTo[0].id].send(JSON.stringify({ type: 'chat', data: data }))
      }
    }
  }
}

// 处理移动
function handleMove(playerId, direction) {
  const player = gameState.players[playerId]
  if (!player) return

  // 使用固定的时间步长而不是累积延迟
  const moveDistance = TANK_SPEED * (UPDATE_INTERVAL / 1000)

  let newX = player.x
  let newY = player.y

  switch (direction) {
    case 'up':
      newY -= moveDistance
      break
    case 'down':
      newY += moveDistance
      break
    case 'left':
      newX -= moveDistance
      break
    case 'right':
      newX += moveDistance
      break
  }

  // 计算边界内的合法位置
  const { x, y } = clampPosition(newX, newY, gameState.width, gameState.height, TANK_SIZE)

  // 创建坦克碰撞矩形
  const tankRect = { x, y, width: TANK_SIZE, height: TANK_SIZE }

  // 检查与障碍物的碰撞
  const obstacleCollision = gameState.obstacles.some(obs => checkCollision(tankRect, obs))
  // 检查与其他玩家的碰撞（排除当前玩家）
  const playerCollision = Object.values(gameState.players)
    .filter(player => player.id !== playerId) // 排除当前玩家
    .some(player => {
      const playerRect = {
        x: player.x,
        y: player.y,
        width: TANK_SIZE,
        height: TANK_SIZE
      }
      return checkCollision(tankRect, playerRect)
    })
  player.angle = getAngleFromDirection(direction)
  if (!obstacleCollision && !playerCollision) {
    player.x = x
    player.y = y
    player.lastUpdate = Date.now() // 记录最后更新时间
  }
}

// 处理射击
function handleShoot(playerId) {
  const player = gameState.players[playerId]
  if (!player || Date.now() - player.lastShot < SHOOT_COOLDOWN) return

  const bullet = {
    id: crypto.randomUUID(),
    owner: playerId,
    x: player.x + TANK_SIZE / 2,
    y: player.y + TANK_SIZE / 2,
    angle: player.angle,
    speed: BULLET_SPEED,
    createdAt: Date.now()
  }

  gameState.bullets.push(bullet)
  player.lastShot = Date.now()
}

// 更新子弹
function updateBullets() {
  gameState.bullets = gameState.bullets
    .map(bullet => ({
      ...bullet,
      x: bullet.x + Math.cos(bullet.angle) * bullet.speed,
      y: bullet.y + Math.sin(bullet.angle) * bullet.speed
    }))
    .filter(bullet => {
      const inBounds = isInBounds(bullet, gameState.width, gameState.height)
      const isExpired = Date.now() - bullet.createdAt > 5000
      return inBounds && !isExpired
    })
}

// 检测碰撞
function checkCollisions() {
  const bulletsToRemove = []
  const hits = new Map()

  gameState.bullets.forEach((bullet, index) => {
    const bulletRect = {
      x: bullet.x - BULLET_SIZE / 2,
      y: bullet.y - BULLET_SIZE / 2,
      width: BULLET_SIZE,
      height: BULLET_SIZE
    }

    // 玩家碰撞检测
    Object.values(gameState.players).forEach(player => {
      if (player.id === bullet.owner) return

      const tankRect = {
        x: player.x,
        y: player.y,
        width: TANK_SIZE,
        height: TANK_SIZE
      }

      if (checkCollision(bulletRect, tankRect)) {
        bulletsToRemove.push(index)
        hits.set(bullet.id, player.id)
      }
    })

    // 障碍物碰撞检测
    if (gameState.obstacles.some(obs => checkCollision(bulletRect, obs))) {
      bulletsToRemove.push(index)
    }
  })

  // 处理命中结果
  hits.forEach((playerId, bulletId) => {
    const bullet = gameState.bullets.find(b => b.id === bulletId)
    if (!bullet) return

    const attacker = gameState.players[bullet.owner]
    const defender = gameState.players[playerId]

    defender.health = Math.max(0, (defender.health || 3) - 1)
    attacker.score = (attacker.score || 0) + 10
    attacker.needsUpdate = true // 标记需要更新
    defender.needsUpdate = true // 标记需要更新

    if (defender.health <= 0) {
      defender.health = 3
      let ranX = Math.floor((Math.random() * 100) % (gameState.width / 60)) * 60
      let ranY = Math.floor((Math.random() * 100) % (gameState.height / 60)) * 60
      while (1) {
        const collision = gameState.obstacles.some(obs => obs.x === ranX && obs.y === ranY)
        if (!collision) {
          break
        }
        ranX = Math.floor((Math.random() * 100) % (gameState.width / 60)) * 60
        ranY = Math.floor((Math.random() * 100) % (gameState.height / 60)) * 60
      }
      defender.x = ranX
      defender.y = ranY
      defender.needsUpdate = true // 标记需要更新
    }
  })

  // 移除被击中的子弹
  gameState.bullets = gameState.bullets.filter((_, index) => !bulletsToRemove.includes(index))
}

// 边界检测
function isInBounds(obj, width, height) {
  const margin = BULLET_SIZE
  return obj.x > -margin && obj.x < width + margin && obj.y > -margin && obj.y < height + margin
}

// 广播游戏状态
function broadcastGameState() {
  const state = {
    type: 'gameState',
    status: gameState.status,
    timestamp: Date.now(),
    players: {},
    bullets: gameState.bullets.map(b => ({
      id: b.id,
      x: b.x,
      y: b.y,
      owner: b.owner
    })),
    obstacles: gameState.obstacles
  }
  // 只包含有变化的玩家
  Object.values(gameState.players).forEach(player => {
    state.players[player.id] = {
      id: player.id,
      playerName: player.playerName,
      x: player.x,
      y: player.y,
      angle: player.angle,
      health: player.health,
      score: player.score,
      color: player.color
    }
  })

  // 广播状态
  wss.clients.forEach(client => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify(state))
    }
  })
}

// 解包二进制数据
function unpackData(unKnowBuffer) {
  let buffer

  // 处理Node.js Buffer
  if (unKnowBuffer instanceof Buffer) {
    buffer = unKnowBuffer.buffer.slice(unKnowBuffer.byteOffset, unKnowBuffer.byteOffset + unKnowBuffer.byteLength)
  }
  // 处理标准ArrayBuffer
  else if (unKnowBuffer instanceof ArrayBuffer) {
    buffer = unKnowBuffer
  } else {
    throw new Error('Unsupported data type: ' + typeof unKnowBuffer)
  }
  const view = new DataView(buffer)

  // 读取类型
  const type = view.getUint8(0)

  // 读取targetId长度
  const targetIdLength = view.getUint8(1)

  // 读取targetId
  const targetIdBytes = new Uint8Array(buffer, 2, targetIdLength)
  const targetId = new TextDecoder().decode(targetIdBytes)

  // 读取数据
  const dataOffset = 2 + targetIdLength
  const dataLength = buffer.byteLength - dataOffset
  const data = buffer.slice(dataOffset)

  return { type, targetId, data }
}

// 处理二进制媒体数据
function handleBinaryMediaData(client, buffer) {
  const senderId = clients.get(client)
  if (!senderId) {
    console.warn('无法识别的媒体数据发送者')
    return
  }

  try {
    const { type, targetId, data } = unpackData(buffer)
    if (targetId) {
      // 检查目标玩家是否存在
      const targetClient = playerMap[targetId]
      if (targetClient) {
        if (targetClient.readyState === WebSocket.OPEN) {
          // 转发完整的二进制数据（保留原始格式）
          targetClient.send(buffer)
        }
      } else {
        console.warn(`目标玩家 ${targetId} 不存在，无法转发媒体数据`)
      }
    }
  } catch (error) {
    console.error('处理二进制媒体数据失败:', error)
  }
}

// 处理JSON格式的状态数据
function handleJsonStatusData(client, message) {
  const senderId = clients.get(client)
  if (!senderId) {
    console.warn('无法识别的状态数据发送者')
    return
  }

  try {
    // 状态数据包含目标ID，直接转发
    if (message.data.targetId) {
      const targetId = message.data.targetId
      if (playerMap[targetId]) {
        const targetClient = playerMap[targetId]
        if (targetClient.readyState === WebSocket.OPEN) {
          // 转发前添加发送者信息
          const forwardMessage = {
            ...message,
            senderId: senderId
          }
          targetClient.send(JSON.stringify(forwardMessage))
        }
      } else {
        console.warn(`目标玩家 ${targetId} 不存在，无法转发状态数据`)
      }
    }
    // 特殊处理通话结束等需要广播的状态
    else if (message.type === 'endCall') {
      Object.values(playerMap).forEach(client => {
        if (client.readyState === WebSocket.OPEN) {
          client.send(JSON.stringify({ ...message, senderId }))
        }
      })
    }
  } catch (error) {
    console.error('处理JSON状态数据失败:', error)
  }
}

// WebSocket 连接处理
wss.on('connection', (client, request) => {
  const playerId = request.url.split('/')[2] || crypto.randomUUID() // 确保有唯一ID
  clients.set(client, playerId)
  console.log(`玩家连接: ${playerId}`)

  if (!gameState.players[playerId]) {
    if (Object.keys(gameState.players).length >= MAX_PLAYERS) {
      client.send(JSON.stringify({ type: 'error', message: '服务器已满' }))
      client.close()
      return
    }
    playerMap[playerId] = client
    const color = `hsl(${Math.random() * 360}, 70%, 60%)`
    let ranX = Math.floor((Math.random() * 100) % (gameState.width / 60)) * 60
    let ranY = Math.floor((Math.random() * 100) % (gameState.height / 60)) * 60
    while (1) {
      const collision = gameState.obstacles.some(obs => obs.x === ranX && obs.y === ranY)
      if (!collision) {
        break
      }
      ranX = Math.floor((Math.random() * 100) % (gameState.width / 60)) * 60
      ranY = Math.floor((Math.random() * 100) % (gameState.height / 60)) * 60
    }
    gameState.players[playerId] = {
      id: playerId,
      x: ranX,
      y: ranY,
      angle: 0,
      health: 3,
      score: 0,
      lastShot: 0,
      lastUpdate: Date.now(),
      needsUpdate: true,
      color,
      playerName: `玩家${playerId.slice(0, 6)}` // 默认名称
    }
  }

  // 发送初始连接信息
  client.send(
    JSON.stringify({
      type: 'connectionSuccess',
      data: {
        playerId: playerId,
        currentPlayers: Object.keys(gameState.players).length
      }
    })
  )

  // 处理客户端消息（区分文本和二进制）
  client.on('message', data => {
    if (data.length > 1000) {
      // 处理音频数据
      handleBinaryMediaData(client, data)
    } else {
      // 处理文本数据
      try {
        const message = JSON.parse(data.toString())
        switch (message.type) {
          // 游戏相关消息
          case 'chat':
            handleChat(message.data)
            break
          case 'move':
            handleMove(playerId, message.data.direction)
            break
          case 'shoot':
            handleShoot(playerId)
            break
          case 'setName':
            gameState.players[playerId].playerName = message.data.playerName
            break
          case 'disconnect':
            client.close()
            break

          // 媒体通话相关状态消息
          case 'videoStatus':
          case 'audioStatus':
          case 'startCall':
          case 'endCall':
            handleJsonStatusData(client, message)
            break

          default:
            console.log(`收到未知消息类型: ${message.type}`)
        }
      } catch (error) {
        client.send(JSON.stringify({ type: 'error', message: '无效消息格式' }))
        console.log('无效消息格式:', error, playerId)
      }
    }
  })

  // 处理连接关闭
  client.on('close', () => {
    console.log(`玩家断开: ${playerId}`)
    clients.delete(client)
    delete gameState.players[playerId]
    delete playerMap[playerId]

    // 广播玩家离开信息
    Object.values(playerMap).forEach(targetClient => {
      if (targetClient.readyState === WebSocket.OPEN) {
        targetClient.send(
          JSON.stringify({
            type: 'playerDisconnect',
            data: { playerId: playerId }
          })
        )
      }
    })

    broadcastGameState()
  })

  // 处理错误
  client.on('error', error => {
    console.error('WebSocket 错误:', error)
  })
})

// 游戏循环
let gameInterval
function startGameLoop() {
  if (gameState.status === 'waiting') {
    gameState.width = 1200
    gameState.height = 600
    gameState.obstacles = generateObstacles(gameState.width, gameState.height)
    gameState.status = '游戏中'
  }

  // 游戏主循环
  gameInterval = setInterval(() => {
    updateBullets()
    checkCollisions()
    broadcastGameState()
  }, UPDATE_INTERVAL)
}

// HTTP服务
app.use(express.static(path.join(__dirname, 'public')))
let flag = 1
// 模拟API接口
app.get('/todos/:id', (req, res) => {
  const id = req.params.id
  console.log(`Received request for /todos/${id}`)
  let timeOUt = 200
  if (flag++ === 1) {
    timeOUt = 3000
  } else {
    flag = 1
  }
  setTimeout(() => {
    res.json({
      userId: 1,
      id: parseInt(id),
      title: `Item ${id}`,
      completed: false
    })
  }, timeOUt) // 延迟模拟网络延迟
})

// 启动服务器
httpServer.listen(PORT, () => {
  console.log(`HTTP 服务器运行在端口 ${PORT}`)
})

wss.on('listening', () => {
  console.log(`WebSocket 服务器运行在端口 3001`)
  startGameLoop()
})

// 异常处理
process.on('unhandledRejection', error => {
  console.error('未处理的拒绝:', error)
  if (gameInterval) clearInterval(gameInterval)
  httpServer.close()
})

process.on('SIGINT', () => {
  console.log('服务器关闭中...')
  if (gameInterval) clearInterval(gameInterval)
  httpServer.close(() => {
    console.log('HTTP 服务器已关闭')
    process.exit(0)
  })
})
